
import java.nio.ByteBuffer;
import java.util.zip.*;

/*
 | Preamble | Start  | Dest Addr | Source Addr | Type/length |     Payload     |   CRC   |
 | 7 bytes  | 1 byte |  6 bytes  |   6 bytes   | 2 bytes     | 46 - 1500 bytes | 4 bytes |
 */
public class EthernetFrame
{
    private byte[] frame;
    private byte[] preamble;
    private byte[] sfd;
    private byte[] destination;
    private byte[] source;
    private byte[] length;
    private byte[] payload;
    private byte[] crc;
    private int[] lengths;
    ByteBuffer buff;

    public EthernetFrame()
    {
    }

    public EthernetFrame(byte[] data)
    {
        frame = data;
        frame = reverseByteArray(frame);
        lengths = computeLengths();
        if (data.length < 72 || data.length > 1526)
        {
            System.out.println("Invalid frame length.  Dropped.");
        }
        else if (!passCRC(getCRC(), computeCRC()))
        {
            System.out.println("Failed CRC.  Dropped.");
        }
        else
        {
            int offset = 0;
            buff = ByteBuffer.allocate(frame.length);
            buff.put(frame);
            buff.get(preamble, offset, lengths[0]).array();
            buff.get(sfd, offset += (lengths[1] - 1), lengths[1]).array();
            buff.get(destination, offset += (lengths[2] - 1), lengths[2]).array();
            buff.get(source, offset += (lengths[3] - 1), lengths[3]).array();
            buff.get(length, offset += (lengths[4] - 1), lengths[4]).array();
            buff.get(payload, offset += (lengths[5] - 1), lengths[5]).array();
            buff.get(crc, offset += (lengths[6] - 1), lengths[6]).array();
        }
    }

    private int[] computeLengths()
    {
        int length;
        ByteBuffer temp = ByteBuffer.allocate(2);
        temp.put(frame, 20, 2);
        length = temp.getInt();
        if (length > 1500)
        {
            System.out.println("Type.  Program Crash.");
        }
        int[] y = new int[7];
        y[0] = 7;
        y[1] = 1;
        y[2] = 6;
        y[3] = 6;
        y[4] = 2;
        y[5] = length;
        y[6] = 4;
        return y;
    }

    private boolean passCRC(long CRC1, long CRC2)
    {
        //change crc to all 0s
        //then run entire thing through crc
        //take remainder and change crc to that
        //check crc to see if all equals 0
        //do not include preamble, SFD, FCS
        return CRC1 == CRC2;
    }

    private int getCRC()
    {
        int value;
        ByteBuffer temp = ByteBuffer.allocate(4);
        temp.put(frame, frame.length - 5, 4);
        value = temp.getInt();
        return value;
    }

    private long computeCRC()
    {
		ByteBuffer temp = ByteBuffer.allocate (14 + lengths [5]);
		buff.get (temp, 8, 14+lengths[5]);
        long CRCvalue;
        CRC32 x = new CRC32();
        x.update(temp);
        CRCvalue = x.getValue();
        return CRCvalue;
    }

    private byte[] reverseByteArray(byte[] byteArray)
    {
        byte [] reversedArray = new byte[byteArray.length];
        for (int x = 0; x < reversedArray.length; x++)
        {
            reversedArray[x] = reverseByte (byteArray[x]);
        }
        return reversedArray;
    }

    private byte reverseByte(byte a)
    {
        byte reversed = 0;
        int[] temp = new int[8];
        for (int x = 0; x < temp.length; x++)
        {
            temp[x] = (a & 1);
            a >>= 1;
        }
        for (int y = 0; y < temp.length; y++)
        {
            reversed <<= 1;
            reversed = (byte) (reversed | temp[y]);
        }
        return reversed;
    }
}
